#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G05 -- S05 ------------------------------------------------------------##
__all__ = []

from .. import formlib as layout
from .. import pilelib as stack
from .. import stackfill as fill
from ..gamelib import info as gi, state, game
from ..hint import CautiousDefaultHint
from ..packlib import info as cs, place
from . import terrace

GT, SL = gi.GT, gi.SL
Wrap = stack.StackWrapper


class DealEachOnceRedealTalon(stack.DealRowRedealTalon):
    """
    Deal each pile once on _row_. Reset and redeal _row_ when gap.
    """

    def deal_cards(self, sound=False):
        """
        deal_cards([sound:bool=False]) -> None

        Deal cards for Game.talon_deal() to other piles. Deal num_cards
        to each pile _row_ once.

        :keyword boolean sound: sound fx?
        """
        num_cards = 0
        game = self.game
        if sound and game.can_play_sound:
            game._play_sound('deal')
        if len(self.cards) == 0:
            num_cards = self._redeal(reverse=True, frames=0)
            game._move_do(a_move.ANextRoundMove(self), reset=True)  # low level
        for r in self._piles:
            cards = min(self.num_cards, len(self.cards))
            for i in range(cards):
                num_cards += self.dealRow([r], sound=False, frames=4)
        return num_cards


class Saxony_Talon(stack.DealReserveTalon):
    """
    Deal 1st 8 pile on _reserve_.
    """

    @property
    def _piles(self):
        """
        _piles -> tup

        :return: deal Game.s.reserves
        :rtype: tuple
        """
        return self.game.s['reserves'][:8]


class AC_RowStack(stack.AC_RowStack):
    """
    Build down in rank by alt color. _BOTTOM_IMAGE_ is shade.
    """
    _BOTTOM_IMAGE_ = 'shade'


class SS_Reserve(stack.SS_RowStack):
    """
    Build down in rank by same suit. _BOTTOM_IMAGE_ is shade. Help str states
    pile is _reserve_.
    """
    _BOTTOM_IMAGE_ = 'shade'

    @property
    def _helpString(self):
        """
        _helpString -> str

        :return: helpful string about pile for its canvas label.
        :rtype:  base_string
        """
        return '{0} {1}'.format(_('Reserve.'),
                                super(SS_Reserve, self)._helpString)


class Tournament(game.Game):
    """
    Finish cards on _foundation_ within 3 turns.

    _row_
      Pile 6 horz. Init deal 4 open cards. Build none. Play top. Gap deal 4
      cards from _talon.

    _reserve_
       Pile 8 over 2 col E W. Init deal 1 card with >=1 ACE  | KING.
       Build none. Play top.

    _talon_
      Pile 1 NW. Deal 4 card on _row_ 4 cards. Gap resets _row_.

    _foundation_
      8 pile horz N _row_. Build in rank by same suit wrap: E up from base
      ACE, W down from base KING. Play top.

    seealso:: <http://en.wikipedia.org/wiki/Tournament_%28solitaire%29>
    ------
    :cvar Stack _talon_: DealEachOnceRedealTalon (max_rounds=3, num_cards=4)
    :cvar Stack _foundations_: stack.SS_Foundation,
      stack.SS_Foundation (dir=-1, base_rank=KING)
    :cvar Stack _row_: stack.BasicRowStack
    :cvar Stack _reserve_: stack.ReserveStack
    """
    __info__ = dict(id=303, name='Tournament', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.MOSTLY_LUCK, version='0.80fc')
    __author__ = 'Skomoroh'
    __credits__ = ['Albert Morehead', 'Geoffrey Mott-Smith']
    __version__ = 7.70

    _talon_ = Wrap(DealEachOnceRedealTalon, max_rounds=3, num_cards=4)
    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=-1),)
    _row_ = stack.BasicRowStack
    _reserve_ = stack.ReserveStack

    def _create_game(self, offset=True, **kw):
        l, s = layout.Layout(self), self.s
        offset = (0, l.YOFFSET if offset else 0)
        self.table_size = (l.XM + 10 * l.XS,
                     max(l.YM + l._get_stack_height(20, l.YS), 2 * l.YM + 5 * l.YS))
        x, y, = l.XM + 2 * l.XS, l.YM
        for _foundation_ in self._foundations_:
            for i in range(l.SUIT):
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                x += l.XS
        x = l.XM + 2 * l.XS
        y += l.YM + l.YS
        for i in range(6):
            s['rows'].append(self._row_(x, y, game=self, offset=offset))
            x += l.XS
        x = l.XM
        dy = y
        for _reserve_ in (self._reserve_,) * 2:
            y = dy
            for i in range(4):
                self.s['reserves'].append(_reserve_(x, y, game=self))
                y += l.YS
            x = self.width - l.XS
        s['talon'] = DealEachOnceRedealTalon(l.XM, l.YM, self, max_rounds=3)
        l.set_ncards_text(s['talon'], 'se')
        l.set_rounds_text(s['talon'], 'ne')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return single ACE and KING in top 8 for _row_ deal.
        """
        if any(c['rank'] in (cs.ACE, cs.KING) for c in cards[-8:]):
            return cards
        return place.MoveToTop(cards, place.AllAceKing, ncards=1)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[reserves] 1 ea.
          - talon deal.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(self.s['reserves'])
        self.talon_deal(sound=False)

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_ with 4 card.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class LaNivernaise(Tournament):
    """
    Finish cards on _foundation_ within 3 turns.

    _row_
      Pile 6 horz. Init deal 4 open cards. Build none. Play top. Gap deal 4
      cards from _talon.

    _reserve_
       Pile 8 over 2 col E W. Init deal 1 card with >=1 ACE  | KING.
       Build none. Play top.

    _talon_
      Pile 1 NW. Deal 4 card on _row_ 4 cards. Gap resets _row_.

    _foundation_
      8 pile horz N _row_. Build in rank by same suit wrap: E up from base
      ACE, W down from base KING. Play top.

    seealso::
      <http://en.wikipedia.org/wiki/Tournament_%28solitaire%29>
    ------
    :cvar Stack _talon_: DealEachOnceRedealTalon (max_rounds=3, num_cards=4)
    :cvar Stack _foundations_: stack.SS_Foundation,
      stack.SS_Foundation (dir=-1, base_rank=KING)
    :cvar Stack _row_: stack.BasicRowStack (max_move=1, max_accept=0)
    :cvar Stack _reserve_: stack.ReserveStack
    """
    __info__ = dict(id=304, name='La Nivernaise', game_type=GT.TWO_DECK,
                    decks=2, redeals=2, skill_level=SL.MOSTLY_LUCK,
                    altnames=('Napoleonʼs Flank',), version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        kw.setdefault('offset', False)
        return super(LaNivernaise, self)._create_game(**kw)


class KingsdownEights(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 horz N. Init deal 1 card. Build down in rank by alt color. Play
      top. Base KING.

    _reserve_
      Pile 8 horz S _row_. Init deal 4 open cards. Build none. Play top.

    _talon_
      Pile 1 NW. Deals 4 card on _reserve_.

     _foundation_
      8 pile over 2 col W. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: stack.DealReserveTalon (num_cards=4)
    :cvar Stack _foundations_: stack.SS_Foundation
    :cvar Stack _row_: AC_RowStack (max_move=1)
    :cvar Stack _reserve_: stack.OpenStack
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=386, name='Kingsdown Eights', game_type=GT.TWO_DECK,
                    decks=2, redeals=0, skill_level=SL.BALANCED,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.DealReserveTalon, num_cards=4)
    _foundation_ = stack.SS_Foundation
    _row_ = Wrap(AC_RowStack, max_move=1)
    _reserve_ = stack.OpenStack
    _hint_ = CautiousDefaultHint

    def _create_game(self, playcards=12, **kw):
        l, s = layout.Layout(self), self.s
        dx = l.XM + l.NDECKS * l.XS
        self.table_size = (dx + 8 * l.XS,
                max(l.YM + l.YS + l._get_stack_height(playcard, l.YS), l.YM + 5 * l.YS))
        x = l.XM
        dy = l.YM + l.YS
        for _foundation_ in (self._foundation,) * l.NDECKS:  # Horz
            y = dy
            for i in l.SUIT:  # Vert
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                y += l.YS
            x += l.XS
        x, y = dx, l.YM
        for i in range(8):  # Horz
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
            x += l.XS
        x = dx
        y += l.YS
        for i in range(8):  # Horz
            s['reserves'].append(self._reserve_(x, y, game=self,
                                             offset=(0, l.YOFFSET)))
            x += l.XS
        s['talon'] = self._talon_(l.XM, l.YM, game=self)
        l.set_ncards_text(s['talon'], 'se')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.
          - talon deal.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()
        self.talon_deal(sound=False)


class Saxony(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 vert W. Init deal 1 card. Build down in rank by same suit. Play
      top.

    _reserve_
       Pile 8 horz S _foundation_ and 4 vert E. Init deal 1 card E. Build
       horz none, vert 1 card. Play top.

    _talon_
      Pile 1 NW. Deal 1 card on horz _reserve_.

    _foundation_
     Pile 8 horz N. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: Saxony_Talon
    :cvar Stack _foundations_: stack.SS_Foundation
    :cvar Stack _row_: SS_Reserve (max_move=1)
    :cvar Stack _reserves_: stack.BasicRowStack, stack.ReserveStack
    """
    __info__ = dict(id=645, name='Saxony', game_type=GT.TWO_DECK, decks=2,
                    redeals=0, skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Saxony_Talon
    _foundation_ = stack.SS_Foundation
    _row_ = Wrap(SS_Reserve, max_move=1)
    _reserves_ = (stack.BasicRowStack, stack.ReserveStack,)

    def _create_game(self, playcards=12, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 11 * l.XS,
                   2 * l.YM + max(l.YS + l._get_stack_height(playcards, l.YS), 5 * l.YS))
        center = lambda p: (self.width - p) / 2
        x, y, = center(l.NSUITS), l.YM
        for i in l.SUITS:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            x += l.XS
        x = center(8)
        y += l.YM + l.YS
        for i in range(8):  # Horz
            s['reserves'].append(self._reserves_[0](x, y, game=self))
            x += l.XS
        x = l.XM
        dy = y
        for i in range(4):  # Vert
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
            y += l.YS
        x, y = self.width - l.XS, dy
        for i in range(4):  # Vert
            s['reserves'].append(self._reserves_[1](x, y, game=self))
            y += l.YS
        s['talon'] = self._talon_(l.XM, l.YM, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[reserves][8:] 1 ea.
          - s[rows] 1 ea.
          - talon deal.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['reserves'][8:], frames=0)
        self.s['talon'].dealRow(frames=0)
        self.talon_deal(sound=False)


class LadiesBattle(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    6 piles deals a card on _row_. 4, column, piles deals a card _reserve_
    left. Queens deal on _foundation_ right. The _talon_ lower right.

    _row_ build down in rank by alt color. Play top. Any gap deals a
    card talon.

    _reserve_
      Pile 4 vert W. Init deal 1 card. Build none. Play top on _foundation_.

    _talon_
       Pile 1 SE. Deal 1 card on _row_.

     _foundation_
      Pile 4 vert E. Init deal base QUEEN. Build up in rank by same suit from
      base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _foundations_: stack.SS_Foundation (base_rank=QUEEN,
      mod=13)
    :cvar Stack _row_: terrace.NoReserve_AC_RowStack (max_move=1, mod=13)
    :cvar Stack _reserve_: stack.OpenStack
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=652, name='Ladies Battle', game_type=GT.ONE_DECK,
                    decks=1, redeals=0, skill_level=SL.MOSTLY_LUCK,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.DealRowTalon
    _foundation_ = Wrap(stack.SS_Foundation, base_rank=cs.QUEEN, mod=13)
    _row_ = Wrap(terrace.NoReserve_AC_RowStack, max_move=1, mod=13)
    _reserve_ = stack.OpenStack
    _hint_ = CautiousDefaultHint

    def _create_game(self, rows=6, reserves=4, playcards=20, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + (rows + 3) * l.XS,
                     max(l.YM + l._get_stack_height(playcards, l.YS),
                         l.YM + (max(reserves. l.NSUITS + 2) * l.YS)))
        x, y = l.XM + 3 * l.XS / 2, l.YM
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        x = l.XM, l.YM + l.YS / 2
        y += l.YS / 2
        for i in range(reserves):  # Vert
            s['reserves'].append(self._reserve_(x, y, game=self))
            y += l.YS
        x, y = self.width - l.XS, l.YM + l.YS / 2
        for suit in l.SUITS:  # Vert
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                   suit=suit))
            y += l.YS
        x, y = self.width - l.XS, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'sw')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return JACK and QUEEN on top by rank, suit for _reserve_ and
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllJackQueen)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[reserves] 1 ea.
          - s[foundations] 1 ea.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['reserves'], frames=0)
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


PROVIDES = [Tournament, LaNivernaise, KingsdownEights, Saxony, LadiesBattle]